﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PSLib.Parsing.Common;
using PSLib.Parsing.Common.Search;
using PSLib.Parsing.Lua.ObjectModel;
using Antlr.Runtime;

namespace PSLib.Parsing.Lua.Context
{
    public class ContextProvider
    {
        internal ContextProvider(LuaParser parser)
        {
            this.Parser = parser;
        }

        public IEnumerable<ContextCompletion> GetContext(ElementPosition position)
        {
            Scope localScope = Parser.ObjectModelProvider.GetLocalScope(position);
            TokensStreamSearchProvider search = new TokensStreamSearchProvider(this.Parser.Input as CommonTokenStream);
            List<ContextCompletion> completions = new List<ContextCompletion>();

            RecognizedToken currentToken = search.Find(position);
            RecognizedToken prevToken = search.Get(currentToken, SearchDirection.Backward);

            if (prevToken != null && prevToken.NativeToken.GetSpan(this.Parser.Input as CommonTokenStream).End == currentToken.NativeToken.GetPosition())
            {
                currentToken = prevToken;
            }

            if (currentToken != null)
            {
                string path = ".";
                if (!currentToken.NativeToken.Text.Contains("\n") && currentToken.Type != RecognizedTokenType.Keyword) path = GetBaseVariablePath(currentToken);
                
                string[] pathStrings = path.Split('|');
                if (pathStrings.Length > 0)
                {
                    Variable variable = localScope.GetVariable(pathStrings[0], false, false);
                    for (int i = 1; i < pathStrings.Length - 1; i++)
                    {
                        ValueReference baseValueReference = variable.GetValue(position);
                        Metatable table = baseValueReference.Value as Metatable;
                        if (table != null && table.Contains(pathStrings[i])) variable = table[pathStrings[i]];
                        else
                        {
                            variable = null;
                            break;
                        }
                    }

                    IEnumerable<Variable> variables = null;
                    if (variable != null)
                    {
                        ValueReference value = variable.GetValue(position, false);
                        if (value != null)
                        {
                            Metatable metaValue = value.Value as Metatable;
                            if (metaValue != null) variables = metaValue.Select(v => v.Value);
                        }
                    }
                    else variables = localScope.GetVariables(position);

                    if (variables != null)
                    {
                        foreach (var item in variables)
                        {
                            ValueReference value = item.GetValue(position,false);
                            if (value == null) continue;
                            CompletionType type = CompletionType.Field;
                            if (value.Type == ValueReferenceType.Function) type = CompletionType.Function;
                            completions.Add(new ContextCompletion(item.Name, item.Name, type));
                        }
                    }
                }
            }
            return completions;
        }

        private string GetBaseVariablePath(RecognizedToken token)
        {
            TokensStreamSearchProvider search = new TokensStreamSearchProvider(this.Parser.Input as CommonTokenStream);
            RecognizedToken prevToken = search.Get(token, SearchDirection.Backward);

            Query wordSequence = new Query(
                QParam.Create("."), 
                QParam.Create(RecognizedTokenType.Word,true)
                );

            Query pointSequence = new Query(
                QParam.Create(".", true)
                );

            Query bracketsSequence = new Query(
                QParam.Create("["), 
                QParam.Create(RecognizedTokenType.String,true), 
                QParam.Create("]")
                );
            Query baseVariable = new Query(
                QParam.Create(RecognizedTokenType.Word,true)
                );

            RecognizedToken queryToken = null;
            string path = ".";
            do
            {
                queryToken = null;
                if (search.Query(token, SearchDirection.Backward, false, wordSequence, out queryToken))
                {
                    path = queryToken.NativeToken.Text + "|" + path;
                    token = search.Get(token, -wordSequence.Sequence.Count);
                }
                else if (search.Query(token, SearchDirection.Backward, false, bracketsSequence, out queryToken))
                {
                    path = queryToken.NativeToken.Text.Trim('"') + "|" + path;
                    token = search.Get(token, -bracketsSequence.Sequence.Count);
                }
                else if (search.Query(token, SearchDirection.Backward, false, pointSequence, out queryToken))
                {
                    token = search.Get(token, -pointSequence.Sequence.Count);
                }
                else if (search.Query(token, SearchDirection.Backward, false, baseVariable, out queryToken))
                {
                    path = queryToken.NativeToken.Text + "|" + path;
                    token = search.Get(token, -baseVariable.Sequence.Count);
                    break;
                }
            } while (queryToken != null);

            return path;
        }
        /*private Variable GetBaseToken(RecognizedToken startToken)
        {
            RecognizedToken result = null;
            TokensStreamSearch search = new TokensStreamSearch(this.Parser.Input as CommonTokenStream);
            
            TokensSearchQuery pointQuery = new TokensSearchQuery(0,TokensLogicType.Word,".");
            //TokensSearchQuery accessQuery = new TokensSearchQuery(0, TokensLogicType.Word, ".");

            if (!search.Search(startToken, SearchDirection.Backward, false, pointQuery, out result))
            { 
            
            }
            return result;
        }*/

        internal LuaParser Parser { get; set; }
    }
}
